/**
 * 
 */
package org.ikross.h2.adapters.dbf;

import java.io.File;
import java.sql.SQLException;
import java.util.Hashtable;

import org.h2.engine.Session;
import org.h2.index.Cursor;
import org.h2.index.ExternCursor;
import org.h2.message.Message;
import org.h2.result.Row;
import org.h2.result.SearchRow;
import org.h2.table.Column;
import org.h2.table.TableExtern;
import org.h2.table.TableExternAdapter;
import org.h2.value.DataType;
import org.h2.value.Value;
import org.h2.value.ValueBoolean;
import org.h2.value.ValueByte;
import org.h2.value.ValueDate;
import org.h2.value.ValueDecimal;
import org.h2.value.ValueDouble;
import org.h2.value.ValueFloat;
import org.h2.value.ValueInt;
import org.h2.value.ValueLong;
import org.h2.value.ValueNull;
import org.h2.value.ValueShort;
import org.h2.value.ValueString;
import org.h2.value.ValueTime;
import org.h2.value.ValueTimestamp;

import com.linuxense.javadbf.DBFException;
import com.linuxense.javadbf.DBFField;
import com.linuxense.javadbf.DBFFileManager;

/**
 * @author falario
 *
 */
public class TableDBF extends TableExternAdapter{
	
	private static final String TYPE_NAME = "dbf";
	
	private DBFFileManager dbfFM = null;
	
	private Column[] columns=null;
	
	private double idCounter = 0;
	
	public static void registerType(){
		TableExtern.registerType(TableDBF.TYPE_NAME, TableDBF.class);
	}

	/**
	 * 
	 */
	public TableDBF() {
		this.setTypeName(TYPE_NAME);
		// TODO Auto-generated constructor stub
	}
	
	private boolean initializeFileManager() throws SQLException{
		if(this.dbfFM==null){
			try{
				this.dbfFM=this.createNewDBFFileManager();
				return true;
			}
			catch(SQLException except){
				throw except;
			}
		}
		else{
			return false;
		}
	}
	
	private DBFFileManager createNewDBFFileManager() throws SQLException{
		try{
			DBFFileManager dbfFM=null;
			String file=String.valueOf(this.params.get(S_FILE));
			dbfFM=new DBFFileManager(new File(file), DBFFileManager.READ_WRITE);
			return dbfFM;
		}
		catch(DBFException except){
			throw Message.convert(except);
		}
	}
	
	private void destroyFileManager(DBFFileManager dbfFM) throws SQLException{
		try {
			dbfFM.close();
		}
		catch (DBFException e) {
			throw Message.convert(e);
		}
		finally{
			dbfFM=null;
		}
	}
	
	public void initialize(TableExtern table, Hashtable parameters, Column[] columns) throws SQLException{
		super.initialize(table, parameters, columns);
		this.checkParameters();
		this.initializeFileManager();
	}

	public void checkParameters() throws SQLException{
		// TODO Auto-generated method stub
		if(!this.params.containsKey(S_FILE)){
			throw new SQLException("Parameter \""+S_FILE+"\" not found");
		}
	}

	public String getParam(String key) throws SQLException {
		String param=String.valueOf(this.params.get(key));
		if (param==null) throw new SQLException("The key"+key+" does not represent any parameter");
		
		return param;
	}

	/* (non-Javadoc)
	 * @see org.h2.table.TableExternAdapter#getData(org.h2.engine.Session, org.h2.result.SearchRow, org.h2.result.SearchRow)
	 */
	public Cursor getData(Session session, SearchRow first, SearchRow last) throws SQLException{
		// TODO Auto-generated method stub
		try{
			ExternCursor cursor=null;
			DBFFileManager dbfFM=null;
			dbfFM=this.createNewDBFFileManager();
			dbfFM.beforeFirst();
			return new CursorDBF(this.tableExtern, dbfFM);
		}
		catch(Exception except){
			throw Message.convert(except);
		}
	}

	/* (non-Javadoc)
	 * @see org.h2.table.TableExternAdapter#closeSource()
	 */
	public boolean closeSource() {
		// TODO Auto-generated method stub
		try{
			this.destroyFileManager(this.dbfFM);
			return true;
		}
		catch(Exception except){
			return false;
		}
	}

	/* (non-Javadoc)
	 * @see org.h2.table.TableExternAdapter#drop()
	 */
	public void drop() {
		// TODO Auto-generated method stub
		//nothing to do
	}

	/* (non-Javadoc)
	 * @see org.h2.table.TableExternAdapter#insertData(org.h2.engine.Session, org.h2.result.Row)
	 */
	public void insertData(Session session, Row row) throws SQLException{
		// TODO Auto-generated method stub
		Object o[] = new Object[row.getColumnCount()+1];
		o[0]=ValueDouble.get(this.idCounter);
		Value v=null;
		try{
			for(int i=0;i<o.length;i++){
				v=row.getValue(i);
				switch(v.getType()){
					case Value.BOOLEAN:
						if(v instanceof ValueBoolean){
							o[i]=v.getBoolean();
						}
						else{
							throw new SQLException(/*ERROR DEL TIPO DE VALUE*/);
						}
						break;
						
					case Value.BYTE:
						if(v instanceof ValueByte){
							o[i]=new Byte(v.getByte());
						}
						else{
							throw new SQLException(/*ERROR DEL TIPO VALUE*/);
						}
						break;
						
					case Value.DECIMAL:
						if(v instanceof ValueDecimal){
							o[i]=v.getBigDecimal();
						}
						else{
							throw new SQLException(/*ERROR DEL TIPO VALUE*/);
						}
						break;
						
					case Value.DATE:
						if(v instanceof ValueDate){
							o[i]=v.getDate();
						}
						else{
							throw new SQLException(/*ERROR DEL TIPO VALUE*/);
						}
						break;
						
					case Value.DOUBLE:
						if(v instanceof ValueDouble){
							o[i]=new Double(v.getDouble());
						}
						else{
							throw new SQLException(/*ERROR DEL TIPO VALUE*/);
						}
					case Value.FLOAT:
						if(v instanceof ValueFloat){
							o[i]=new Float(v.getFloat());
						}
						else{
							throw new SQLException(/*ERROR DEL TIPO VALUE*/);
						}
						break;
						
					case Value.INT:
						if(v instanceof ValueInt){
							o[i]=new Integer(v.getInt());
						}
						else{
							throw new SQLException(/*ERROR DEL TIPO VALUE*/);
						}
						break;
						
					case Value.LONG:
						if(v instanceof ValueLong){
							o[i]=new Long(v.getLong());
						}
						else{
							throw new SQLException(/*ERROR DEL TIPO VALUE*/);
						}
						break;
						
					case Value.NULL:
						if(v instanceof ValueNull){
							o[i]=null;
						}
						else{
							throw new SQLException(/*ERROR DEL TIPO VALUE*/);
						}
						break;
						
					case Value.SHORT:
						if(v instanceof ValueShort){
							o[i]=new Short(v.getShort());
						}
						else{
							throw new SQLException(/*ERROR DEL TIPO VALUE*/);
						}
						break;
						
					case Value.STRING_IGNORECASE:
					case Value.STRING:
						if(v instanceof ValueString){
							o[i]=v.getString();
						}
						else{
							throw new SQLException(/*ERROR DEL TIPO VALUE*/);
						}
						break;
						
					case Value.TIME:
						if(v instanceof ValueTime){
							o[i]=v.getTime();
						}
						else{
							throw new SQLException(/*ERROR DEL TIPO VALUE*/);
						}
						break;
						
					case Value.TIMESTAMP:
						if(v instanceof ValueTimestamp){
							o[i]=v.getTimestamp();
						}
						else{
							throw new SQLException(/*ERROR DEL TIPO VALE*/);
						}
						break;
					
					case Value.ARRAY:
					case Value.BLOB:
					case Value.BYTES:
					case Value.CLOB:
					case Value.JAVA_OBJECT:
					case Value.RESULT_SET:
					case Value.TYPE_COUNT:
					case Value.UNKNOWN:
					case Value.UUID:
						throw new SQLException(/*NI IDEA DE QUE HACER EN ESTOS CASOS*/);
						
					default:
						throw new SQLException(/*TIPO DE DATO DESCONOCIDO*/);
				}
			}
			this.dbfFM.addRecord(o);
		}
		catch(DBFException except){
			throw Message.convert(except);
		}
		catch(SQLException except){
			throw except;
		}
	}

	/* (non-Javadoc)
	 * @see org.h2.table.TableExternAdapter#removeData(org.h2.engine.Session, org.h2.result.Row)
	 */
	public void removeData(Session session, Row row) throws SQLException{
		// TODO Auto-generated method stub
		try{
			this.dbfFM.deleteRecord(row.getPos());
		}
		catch(DBFException except){
			Message.convert(except);
		}
	}

	/* (non-Javadoc)
	 * @see org.h2.table.TableExternAdapter#canGetRowCount()
	 */
	public boolean canGetRowCount() {
		// TODO Auto-generated method stub
		return true;
	}

	/* (non-Javadoc)
	 * @see org.h2.table.TableExternAdapter#getRowCount()
	 */
	public int getRowCount() {
		// TODO Auto-generated method stub
		return this.dbfFM.getNumberOfRecord();
	}

	/* (non-Javadoc)
	 * @see org.h2.table.TableExternAdapter#needRebuild()
	 */
	public boolean needRebuild() {
		// TODO Auto-generated method stub
		return false;
	}

	/* (non-Javadoc)
	 * @see org.h2.table.TableExternAdapter#canGetFirstOrLast()
	 */
	public boolean canGetFirstOrLast() {
		// TODO Auto-generated method stub
		return false; //De momento, cuando sepa como montar un Value que vaya devolviendo valores por posicion pasara a true
	}

	/* (non-Javadoc)
	 * @see org.h2.table.TableExternAdapter#findFirstOrLast(org.h2.engine.Session, boolean)
	 */
	public Value findFirstOrLast(Session session, boolean first) {
		// TODO Auto-generated method stub
		return null;
	}

	public boolean canGetRowPos() {
		// TODO Auto-generated method stub
		return true;
	}

	public int getRowPos() {
		// TODO Auto-generated method stub
		try{
			return this.dbfFM.getPos();
		}
		catch(DBFException except){
			return -1;
		}
	}

	public Column[] getColumns() throws SQLException {
		// TODO Auto-generated method stub
		if(this.columns==null){
			try {
				//Obtener las columnas a partir del fichero
				this.initializeFileManager();
				Column[] columns=new Column[this.dbfFM.getFieldCount()];
				
				//columns[0]=new Column("ID", Types.DOUBLE, 0, 0);
				
				int fieldsCount = this.dbfFM.getFieldCount();
				DBFField field=null;
				int valueFieldType=-1;
				long fieldDecimalCount=-1;
				int fieldScale=-1;
				
				for(int i=0;i<fieldsCount;i++){
					field=this.dbfFM.getField(i);
					//fieldType=TableExternAdapter.fromSQLTypeToValueType(field.getSQLType());
					valueFieldType=DataType.convertSQLTypeToValueType(field.getSQLType());
					DataType dataType=DataType.getDataType(valueFieldType);
					switch(valueFieldType){
						case Value.STRING:
						case Value.STRING_IGNORECASE:
							fieldDecimalCount=dataType.defaultPrecision;
							fieldScale=dataType.defaultScale;
							break;
						default:
							fieldDecimalCount=field.getDecimalCount();
							fieldScale=dataType.defaultScale;
					}
					columns[i]=new Column(field.getName(), valueFieldType, fieldDecimalCount, fieldScale);
				}

				return columns;
			} catch (DBFException e) {
				throw Message.convert(e);
			}
			catch(Exception except){
				return null;
			}
		}
		else{
			return this.columns;
		}
	}

	public void setColumns(Column[] columns) {
		// TODO Auto-generated method stub
		this.columns=columns;
	}
}